package com.wodan.zhongjinjicang.common.orm.spring;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

import com.wodan.zhongjinjicang.common.orm.command.SqlCommandExecutor;
import com.wodan.zhongjinjicang.common.orm.command.SqlCommandExecutorRegistry;
import com.wodan.zhongjinjicang.common.orm.metadata.MapperInterfaceMetaData;
import com.wodan.zhongjinjicang.common.orm.metadata.MetaDataFactory;
import com.wodan.zhongjinjicang.common.orm.metadata.MethodMetaData;
import com.wodan.zhongjinjicang.common.orm.metadata.TableMetaData;
import com.wodan.zhongjinjicang.common.orm.utils.ExecTimeRecordCollection;
import com.wodan.zhongjinjicang.common.orm.utils.ReflectionUtils;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.reflection.ExceptionUtil;
import org.springframework.util.Assert;



/**
 * 通用dao的代理
 * 
 * @ClassName: RdsCommonDaoProxy
 * @author Administrator
 * @date 2015-11-6 下午4:07:05
 * @history
 * 
 * @param <T>
 */
public class RdsCommonDaoProxy<T> implements InvocationHandler {
	/**
	 * sql执行者的registry
	 */
	private final SqlCommandExecutorRegistry sqlCommandExecutorRegistry;

	/**
	 * 被代理的mybatis mapper实现类
	 */
	private final T delegateMybatisMapperProxy;

	/**
	 * 接口操作的实体类类型
	 */
	private final Class<?> mapperInterfaceBeanType;

	/**
	 * 被代理接口的源数据
	 */
	private final MapperInterfaceMetaData mapperInterfaceMetaData;

	private ExecTimeRecordCollection execTimeRecordCollection;

	public RdsCommonDaoProxy(SqlCommandExecutorRegistry sqlCommandExecutorRegistry, Class<T> mapperInterface,
			T delegateMybatisMapperProxy) {
		Assert.notNull(sqlCommandExecutorRegistry);
		Assert.notNull(mapperInterface);
		Assert.notNull(delegateMybatisMapperProxy);

		this.sqlCommandExecutorRegistry = sqlCommandExecutorRegistry;
		this.mapperInterfaceBeanType = ReflectionUtils.getInterfaceGenericType(mapperInterface, 0);
		this.delegateMybatisMapperProxy = delegateMybatisMapperProxy;
		this.mapperInterfaceMetaData = MetaDataFactory.createMapperInterfaceMetaData(mapperInterface);
	}

	public void setExecTimeRecordCollection(ExecTimeRecordCollection execTimeRecordCollection) {
		this.execTimeRecordCollection = execTimeRecordCollection;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if (Object.class.equals(method.getDeclaringClass())) {
			try {
				return method.invoke(this, args);
			} catch (Throwable t) {
				throw ExceptionUtil.unwrapThrowable(t);
			}
		}

		long startTime = System.currentTimeMillis();
		try {
			if (isInterceptByOrmFramework(method, args)) {
				SqlCommandExecutor executor = sqlCommandExecutorRegistry.getByMethod(method);
				if (executor == null) {
					Method superClassMethod = mapperInterfaceMetaData.getCompatibleSuperClassMethod(method);
					if (superClassMethod != null) {
						executor = sqlCommandExecutorRegistry.getByMethod(superClassMethod);
					}
				}

				MethodMetaData methodMetaData = mapperInterfaceMetaData.getMethodMetaData(method);
				if (executor == null) {
					SqlCommandType sqlCommandType = methodMetaData.getSqlCommandType();
					executor = sqlCommandExecutorRegistry.getBySqlType(sqlCommandType);
				}

				if (executor != null) {
					TableMetaData tableMetaData = MetaDataFactory.createTableMetaData(mapperInterfaceBeanType);
					return executor.execute(tableMetaData, methodMetaData, args);
				}
			}

			return method.invoke(delegateMybatisMapperProxy, args);
		} finally {
			long endTime = System.currentTimeMillis();
			execTimeRecordCollection.recordExecTime("DAO方法执行", method.toGenericString(), endTime - startTime);
		}
	}

	private boolean isInterceptByOrmFramework(Method method, Object[] args) {
		if (!isOrmFrameworkMethod(method, args)) {
			return false;
		}

		if (!MetaDataFactory.isOrmEntity(mapperInterfaceBeanType)) {
			return false;
		}

		return true;
	}

	private boolean isOrmFrameworkMethod(Method method, Object[] args) {
		if (!mapperInterfaceMetaData.isOrmFrameworkSubClass()) {
			return false;
		}

		if (mapperInterfaceMetaData.isAutoGenernateSql(method)) {
			return true;
		}

		if (mapperInterfaceMetaData.isOrmFrameworkMethod(method)) {
			return true;
		}

		return false;
	}
}
